/**
 * 2017年12月14日
 */
package exp.paper;

import java.io.Serializable;
import java.util.List;

import org.slf4j.Logger;

import exp.algorithm.sic.FeatureExtractor;
import exp.algorithm.sic.InstanceFeatureVectors;
import exp.algorithm.sic.PeakFinderType;
import exp.algorithm.sic.VectorMakerType;
import exp.algorithm.sic.feature.FeatureVector;
import exp.algorithm.sic.weight.ReliefFWeight;
import exp.paper.api.FeatureTransformer;
import exp.util.DatasetsUtil;
import exp.util.MatrixUtil;
import weka.core.DenseInstance;
import weka.core.Instance;
import weka.core.Instances;

/**
 * 3.4.2
 * @author Alex
 *
 */
public class WFST implements FeatureTransformer,Serializable{
	private static final long serialVersionUID = 6637468864387641317L;
	Logger log = org.slf4j.LoggerFactory.getLogger(WFST.class);
	public WFST() {
		this(5);
	}
	
	public WFST(int k) {
		this.topK = k;
	}
	
	ReliefFWeight rf = new ReliefFWeight();
	FeatureExtractor sc = new FeatureExtractor(PeakFinderType.EXTREME,VectorMakerType.ORIGINAL);
	double weight [];
	int classValue [] ; 
	Instances bytypes[];
	int topK = 5;
	int instanceFeatureKeepNumber = -1;
	Instances transformedHeader  = null;
//	int instanceAtt = 1;
	@Override
	public Instances transformInstances(Instances insts) throws Exception {
		insts = new Instances(insts);
		double [] priors = calcPriors(insts);
		//提取特征向量
		sc.transform(insts);
		//训练集提取到的特征向量结果
		List<InstanceFeatureVectors> ifvs = sc.getInstanceVectors();
		//计算权重,并得到特征向量权重及特征向量所属的类
		rf.input(ifvs, insts.classAttribute().numValues(), insts.classAttribute(),priors);
		this.weight = rf.getWeights();
		this.classValue = rf.getCorrespodingClassValue();
		final Instances [] bytypes = new Instances[insts.numClasses()];
		
		for(int i=0;i<bytypes.length;i++){
			//长度为特征向量的长度,加上一个权重值
			bytypes[i] = DatasetsUtil.initInstancesWithParmas("by_type_"+i, ifvs.get(0).vectorLength + 1 , "_vec_", 100, false,null);
		}
		iteratorFeatrueVector(ifvs, (FeatureVector fv,double classVal,int seq,int totalSeq)->{
			Instances in  = bytypes[(int)classVal];
			float vals []  = fv.toFloatArray();
			double weight = this.weight[totalSeq];
			double [] instVals = new double[vals.length+1];
			instVals[instVals.length-1] = weight;
			System.arraycopy(MatrixUtil.floatArrayToDoubleArray(vals), 0, instVals, 0, vals.length);
			DenseInstance di = new DenseInstance(1.0,instVals);
			in.add(di);
			di.setDataset(in);
		});
		for(int i = 0;i<bytypes.length;i++){
			//升序,应该修改为降序
			bytypes[i].sort(bytypes[i].numAttributes()-1);
			reverseInstances(bytypes[i]);
		}
		this.bytypes = bytypes;
		this.instanceFeatureKeepNumber = sc.getmaxFeatureNum();
		int attnum = this.instanceFeatureKeepNumber * topK * this.bytypes.length;
		Instances convertedInst = DatasetsUtil.initInstancesWithParmas("finalinstnaces",attnum, "_final_", 100, true, insts.classAttribute());
		for(int i = 0 ;i<ifvs.size();i++){
			InstanceFeatureVectors ifv = ifvs.get(i);
			double classValue = ifv.classValue;
			FeatureVector [] fvs = getRandomNFeature(ifv);
			Instance inst = fillRandomVectorIntoInstance(convertedInst, fvs, classValue);
			convertedInst.add(inst);
		}
		this.transformedHeader = convertedInst;
		return convertedInst ;
	}
	
	void NORM(Instances [] bytypes){
		
	}
	static class Normer{
		double total;
		int times;
		public void add(double delta){
			total += delta;
			times ++;
		}
		public void transform(){
			
		}
	}
	
	@Override
	public Instance transformInstance(Instance inst) throws Exception {
		InstanceFeatureVectors ifv = sc.detectFeatureVectorsFromInstance(inst);
		int attnum = this.instanceFeatureKeepNumber * topK * this.bytypes.length + 1;
		FeatureVector fvs [] = ifv.vecs.toArray(new FeatureVector[ifv.vecs.size()]);
		double arr[] = new double[attnum];
//		fill(arr, fvs, bytypes, this.topK, -1.0);
		//inst不知道其类标,就设置成原来的....设置成-1.0出错...
		fill(arr, fvs,this.instanceFeatureKeepNumber, bytypes, this.topK, inst.classValue());
		DenseInstance di = new DenseInstance(1.0,arr);
		di.setDataset(this.transformedHeader);;
		return di;
	}
	
	private final static void reverseInstances(Instances insts){
		int num = insts.numInstances();
		int i = 0 ,j = num-1;
		while(i<j){
			Instance insti = insts.instance(i);
			Instance instj = insts.instance(j);
			double arri[] = insti.toDoubleArray();
			for(int k = 0 ;k<arri.length;k++){
				insti.setValue(k, instj.value(k));
				instj.setValue(k, arri[k]);
			}
			i++;
			j--;
		}
		
	}
	//每个特征向量与bytypes中的每个实例求距离,形成最终向量
	private static void fill(double[] arr,FeatureVector fvs[],int maxKept,Instances[]bytypes,int topK,double classValue){
		int idx = 0;
		for(int i = 0 ;i<maxKept && i<fvs.length && fvs[i]!=null ; i ++ ){
			for(int j=0;j<bytypes.length;j++){
				for(int k = 0;k<topK && k< bytypes[j].numInstances();k++){
					arr[idx++] = distance(fvs[i],bytypes[j].instance(k));
				}
			}
		}
		arr[arr.length-1] = classValue;	
	}
	Instance fillRandomVectorIntoInstance(Instances insts,FeatureVector[]vecs,double classValue){
		double vals[] = new double[insts.numAttributes()];
		fill(vals,vecs,this.instanceFeatureKeepNumber,this.bytypes,this.topK,classValue);
		DenseInstance di = new DenseInstance(1.0,vals);
		di.setDataset(insts);
		return di;
	}
	static double distance(FeatureVector obj1,Instance obj2){
		float v1[] = obj1.toFloatArray();
		double v2[] = obj2.toDoubleArray();
		double dist = 0.0;
		for(int i = 0 ;i<v1.length;i++){
			dist += Math.pow(v1[i]-v2[i], 2);
		}
		return Math.sqrt(dist);
	}
	FeatureVector[] getRandomNFeature(InstanceFeatureVectors ifv){
		int index [] = MatrixUtil.getRandomArrayWithLength(Math.min(this.instanceFeatureKeepNumber,ifv.vecs.size()));
		if(ifv.vecs.size() == 0) return new FeatureVector[]{};
		FeatureVector []res = new FeatureVector[index.length];
		for(int i=0;i<index.length;i++){
			res[i] = ifv.vecs.get(index[i]);
		}
		//取一个实例中的几个向量? 设为N
		//向量长度是 N * bytypes.length * topK
		
		return res;
		
	}
	double[] calcPriors(Instances instances){
		double []priors = new double[instances.numClasses()];
		for(Instance inst:instances){
	      if (!inst.classIsMissing()) {
	    	  priors[(int)inst.classValue()]++;
	      }
		}
		for(int i = 0 ;i<priors.length;i++){
			priors[i] = priors[i]/instances.numInstances();
		}
		return priors;
	}
	
	public static interface FVCB{
		void doWhat(FeatureVector fv,double classVal,int seq,int totalSeq);
	}
	public static void iteratorFeatrueVector(List<InstanceFeatureVectors> vecs,FVCB fvcb){
		int instNum = vecs.size();
		int seq = 0;
		for(int i = 0 ; i< instNum;i++){
			InstanceFeatureVectors ifv = vecs.get(i);
			double classValue = ifv.classValue;
			for(int j=0;j<ifv.vecs.size();j++){
				FeatureVector fv = ifv.vecs.get(j);
				fvcb.doWhat(fv, classValue, j, seq);
				seq ++ ;
			}
		}
	}

	@Override
	public void rest() throws Exception {
		this.bytypes = null;
		this.classValue = null;
		this.rf = new ReliefFWeight();
		//this.sc = new SiftConverter(PeakFinderType.DENSE,VectorMakerType.STASTICS);
		this.transformedHeader = null;
		this.weight = null;
	}
	
}
